package com.demo.utils;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPubSub;
import redis.clients.jedis.Pipeline;
import redis.clients.jedis.Response;

public class Redis {
	private static Logger logger = Logger.getLogger(Redis.class);
	
	private static  JedisPool jedisPool;
	
	@Autowired
	public  void setJedisPool(JedisPool jedisPool) {
		Redis.jedisPool = jedisPool;
	}

	private  static Jedis getJedis() {
		Jedis jedis = null;		
        try {  
        	jedis = jedisPool.getResource();
        	
        } catch (Exception e) {  
        	logger.error("Connection Redis fail. Error: " + e.getMessage());
        	e.printStackTrace();
        }
        
        return jedis;
	}
	
	/**
	 * 发布消息
	 * @param channel 频道
	 * @param message 消息
	 */
	public static void publish(String channel, String message) {
		if (Check.nullOrEmpty(channel) || Check.nullOrEmpty(message)) {
			return;
		}
		
		Jedis jedis = getJedis();
		try {
			if (jedis != null) {
				jedis.publish(channel, message);
			}
		} catch (Exception e) {
			logger.error(String.format("CALL publish FAIL. Channel:%s, Message:%s, Error: ", 
					channel, message, e.getMessage()));
			e.printStackTrace();		
		} finally {
			R.closeQuietly(jedis);
		}	
	}
	
	/**
	 * 订阅消息
	 * @param sub 订阅处理
	 * @param channels 频道数组
	 */
	public static void subscribe(JedisPubSub sub, String...channels) {
		if (sub == null || Check.nullOrEmpty(channels)) {
			return;
		}
		
		Jedis jedis = getJedis();
		try {
			if (jedis != null) {
				jedis.subscribe(sub, channels);
			}
		} catch (Exception e) {
			logger.error(String.format("CALL subscribe FAIL. Channel:%s, Error: ", 
				channels, e.getMessage()));
			e.printStackTrace();		
		} finally {
			R.closeQuietly(jedis);
		}	
	}
	
	/**
	 * 设置键值过期时间
	 * @param key 键值
	 * @param seconds 过期时间，单位: 秒
	 * @return 操作结果<br>
	 * 	true 操作成功<br>
	 * 	false 操作失败
	 */
	public static boolean expire(String key, int seconds) {
	    if (Check.nullOrEmpty(key) || seconds <= 0) {
			return false;
		}
			
		Jedis jedis = getJedis();
		try {
			if (jedis != null) {
				return jedis.expire(key, seconds) == 1; 				
			}
		} catch (Exception e) {
			logger.error("CALL expire FAIL. Error: " + e.getMessage());
			e.printStackTrace();
		} finally {
			R.closeQuietly(jedis);
		}
			
		return false;
	}
	
	/**
	 * 返回指定键值剩余过期时间
	 * @param key 键值
	 * @return 剩余过期时间，单位秒
	 */
	public static long getExpireTime(String key) {
	    if (Check.nullOrEmpty(key)) {
            return -1;
        }
        
        Jedis jedis = getJedis();        
        try {
            if (jedis != null) {
                return jedis.ttl(key);
            }
        } catch (Exception e) {
            logger.error("CALL existKey FAIL. Error: " + e.getMessage());
            e.printStackTrace();
        } finally {
            R.closeQuietly(jedis);   
        }
        
        return -1;
	}
	
	public static Set<String> keys(String pattern) {
		if (Check.nullOrEmpty(pattern)) {
			return Collections.emptySet();
		}

		Jedis jedis = getJedis();

		try {
			if (jedis != null) {
				return jedis.keys(pattern);
			}			
		} catch (Exception e) {
			logger.error(String.format("CALL keys(%s) FAIL. Error: %s", 
				pattern, e.getMessage()));
		} finally {
			R.closeQuietly(jedis);
		}
		
		return Collections.emptySet();
	}
	
	/**
	 * 判断KEY是否存在
	 * @param key REDIS KEY
	 * @return 判断结果<br>
	 * 	true 存在<br>
	 * 	false 不存在<br>
	 */
	public static boolean existKey(String key) {
	    if (Check.nullOrEmpty(key)) {
	        return false;
	    }
	    
	    Jedis jedis = getJedis();
	    
	    try {
	        if (jedis != null) {
	            return jedis.exists(key);
	        }
        } catch (Exception e) {
            logger.error("CALL existKey FAIL. Error: " + e.getMessage());
            e.printStackTrace();
        } finally {
            R.closeQuietly(jedis);   
        }
	    
	    return false;
	}
	
	/**
	 * 判断KEY是否存在
	 * @param key REDIS KEY
	 * @return 判断结果<br>
	 * 	true 存在<br>
	 * 	false 不存在<br>
	 */
	public static boolean  existKey(byte[] key) {
		return !Check.nullOrEmpty(key) 
			? existKey(new String(key))
			: false;
	}
	
	/**
	 * 删除REDIS键值
	 * @param key REDIS键值
	 * @return 操作结果<br>
	 * 	true 操作成功<br>
	 * 	false 操作失败
	 */
	public static boolean del(byte[] key) {
		return !Check.nullOrEmpty(key) ? del(new String[]{new String(key)}) : true;
	}
	
	/**
	 * 删除REDIS键值
	 * @param key REDIS键值
	 * @return 操作结果<br>
	 * 	true 操作成功<br>
	 * 	false 操作失败
	 */
	public static boolean del(String key) {
		return !Check.nullOrEmpty(key) ? del(new String[]{key}) : true;
	}
	
	/**
	 * 删除REDIS键值
	 * @param key REDIS键值数组
	 * @return 操作结果<br>
	 * 	true 操作成功<br>
	 * 	false 操作失败
	 */
	public static boolean del(String[] keys) {
	    if (Check.nullOrEmpty(keys)) {
	        return true;
	    }
	    
	    Jedis jedis = getJedis();	    
	    try {
	        if (jedis != null) {
	        	Long v = jedis.del(keys);
	            return (v != null) && (v >= 0);
	        }
        } catch (Exception e) {
            logger.error("CALL del FAIL. Error: " + e.getMessage());
            e.printStackTrace();
        } finally {
            R.closeQuietly(jedis);   
        }
	    
	    return false;
	}
	
	/**
	 * 删除REDIS键值
	 * @param key REDIS键值集合
	 * @return 操作结果<br>
	 * 	true 操作成功<br>
	 * 	false 操作失败
	 */
	public static boolean del(Collection<String> keys) {
		return !Check.nullOrEmpty(keys) ? del(keys.toArray(new String[]{})) : false;
	}
	
	public static String get(String key) {
		if (Check.nullOrEmpty(key)) {
			return "";
		}
		
		Jedis jedis = getJedis();
		try {
			String s = jedis.get(key);
			return s != null ? s : "";
		} catch (Exception e) {
            logger.error("CALL get FAIL. Error: " + e.getMessage());
            e.printStackTrace();
        } finally {
            R.closeQuietly(jedis);   
        }
		
		return "";		
	}
	
	public static boolean set(String key, String value) {
		if (Check.nullOrEmpty(key)) {
			return false;
		}
		
		Jedis jedis = getJedis();
		try {
			return jedis.set(key, value).equalsIgnoreCase(key);
		} catch (Exception e) {
            logger.error("CALL set FAIL. Error: " + e.getMessage());
            e.printStackTrace();
        } finally {
            R.closeQuietly(jedis);   
        }
		
		return false;
	}
	
	public static long incr(String key) {
		if (Check.nullOrEmpty(key)) {
			return 0L;
		}
		
		Jedis jedis = getJedis();
		try {
			return jedis.incr(key);
		} catch (Exception e) {
            logger.error("CALL incr FAIL. Error: " + e.getMessage());
            e.printStackTrace();
        } finally {
            R.closeQuietly(jedis);   
        }
		
		return 0L;		
	}
	
	public static long incrBy(String key, long value) {
		if (Check.nullOrEmpty(key)) {
			return 0L;
		}
		
		Jedis jedis = getJedis();
		try {			
			return jedis.incrBy(key, value);
		} catch (Exception e) {
            logger.error("CALL incrBy FAIL. Error: " + e.getMessage());
            e.printStackTrace();
        } finally {
            R.closeQuietly(jedis);   
        }
		
		return 0L;			
	}
	
	public static long hincrBy(String key, String field, long value) {
		if (Check.nullOrEmpty(key) || Check.nullOrEmpty(field)) {
			return 0L;
		}
		
		Jedis jedis = getJedis();
		try {			
			return jedis.hincrBy(key, field, value);
		} catch (Exception e) {
            logger.error("CALL hincrBy FAIL. Error: " + e.getMessage());
            e.printStackTrace();
        } finally {
            R.closeQuietly(jedis);   
        }
		
		return 0L;			
	}
	
	public static long decr(String key) {
		if (Check.nullOrEmpty(key)) {
			return Long.MIN_VALUE;
		}
		
		Jedis jedis = getJedis();
		try {
			return jedis.decr(key);
		} catch (Exception e) {
            logger.error("CALL decr FAIL. Error: " + e.getMessage());
            e.printStackTrace();
        } finally {
            R.closeQuietly(jedis);   
        }
		
		return Long.MIN_VALUE;		
	}
	
	public static long decrBy(String key, long value) {
		if (Check.nullOrEmpty(key)) {
			return Long.MIN_VALUE;
		}
		
		Jedis jedis = getJedis();
		try {
			return jedis.decrBy(key, value);
		} catch (Exception e) {
            logger.error("CALL decrBy FAIL. Error: " + e.getMessage());
            e.printStackTrace();
        } finally {
            R.closeQuietly(jedis);   
        }
		
		return Long.MIN_VALUE;		
	}

	public static boolean lpush(String key, Collection<String> values) {
		if (key == null || key.trim().length() == 0 
			|| values == null || values.size() == 0) {
			return false;
		}
			
		Jedis jedis = getJedis();

		Pipeline pipeline = null;
		try {
			if (jedis != null) {
				pipeline = jedis.pipelined();
				pipeline.lpush(key, values.toArray(new String[0]));
				pipeline.sync();
				
				return true;				
			}
		} catch (Exception e) {
			logger.error("CALL lpush FAIL. Error: " + e.getMessage());
			e.printStackTrace();
		} finally {
			if (pipeline != null) {
				pipeline.clear();
			}
			
			R.closeQuietly(pipeline);
			R.closeQuietly(jedis);
		}
		
		return false;
	}
	
	public static boolean lpush(String key, String value) {
		return lpush(key, Arrays.asList(value));
	}
	
	public static boolean lpush(byte[] key, Collection<byte[]> values) {
		if (Check.nullOrEmpty(key) || Check.nullOrEmpty(values)) {
			return false;
		}
			
		Jedis jedis = getJedis();

		Pipeline pipeline = null;
		try {
			if (jedis != null) {
				pipeline = jedis.pipelined();
				
				byte[][] bs = new byte[values.size()][];

				int i = 0;
				for (byte[] data : values) {
					if (data != null) {
						bs[i] = data;
						i++;
					}				
				}
				
				pipeline.lpush(key, bs);
				pipeline.sync();
				
				return true;				
			}
		} catch (Exception e) {
			logger.error("CALL lpush FAIL. Error: " + e.getMessage());
			e.printStackTrace();
		} finally {
			if (pipeline != null) {
				pipeline.clear();
			}
			
			R.closeQuietly(pipeline);
			R.closeQuietly(jedis);
		}
		
		return false;
	}

	public static boolean lpush(byte[] key, byte[] value) {
	    return lpush(key, value != null ? Arrays.asList(value) : null);
	}
	
	public static boolean lpush(Map<byte[], byte[]> map) {
		if (Check.nullOrEmpty(map)) {
			return false;
		}
		
		Jedis jedis = getJedis();
		Pipeline pipeline = null;
		try {
			if (jedis != null) {
				pipeline = jedis.pipelined();
				for (byte[] key : map.keySet()) {
					pipeline.lpush(key, map.get(key));
				}
				pipeline.sync();

				return true;
			}
		} catch (Exception e) {
			logger.error("CALL lpush FAIL. Error: " + e.getMessage());
			e.printStackTrace();
		} finally {
			if (pipeline != null) {
				pipeline.clear();
			}
			
			R.closeQuietly(pipeline);
			R.closeQuietly(jedis);
		}
		
		return false;		
	}
	
	public static List<byte[]> lpop(byte[] key, int size) {		
		if (Check.nullOrEmpty(key) || size <= 0) {
			return Collections.emptyList();
		}

		Jedis jedis = getJedis();
		Pipeline pipe = null;
		try {
			if (jedis != null) {
			    if (!jedis.exists(key)) {
			        return Collections.emptyList();
			    }
			    
				pipe = jedis.pipelined();
				
				List<Response<byte[]>> rsps = new LinkedList<>();
				for (int i = 0; i < size; i++) {
					Response<byte[]> rsp = pipe.lpop(key);
					if (rsp == null) {
						break;
					}
					
					rsps.add(rsp);
				}
				pipe.sync();
				
				List<byte[]> list = new LinkedList<>();
				for (Response<byte[]> r : rsps) {
					byte[] arr = r.get();
					if (arr == null || arr.length == 0) {
						break;
					}
					
					list.add(arr);
				}
				
				return list;
			}
		} catch (Exception e) {
			logger.error("CALL lpush FAIL. Error: " + e.getMessage());
			e.printStackTrace();
		} finally {
			if (pipe != null) {
				pipe.clear();
			}
			
			R.closeQuietly(pipe);
			R.closeQuietly(jedis);
		}
		
		return Collections.emptyList();
	}
	
	public static byte[] lpop(byte[] key) {
		List<byte[]> list = lpop(key, 1);
		return !Check.nullOrEmpty(list) ? list.get(0) : null;
	}
	
	public static List<String> lpop(String key, int size) {
		if (Check.nullOrEmpty(key) || size <= 0) {
			return Collections.emptyList();
		}
		 		
		Jedis jedis = getJedis();
		Pipeline pipe = null;
		try {
			if (jedis != null) {
			    if (!jedis.exists(key)) {
                    return Collections.emptyList();
                }
			   
			    pipe = jedis.pipelined();
			    
				List<Response<String>> response = new LinkedList<>();				
				for (int i = 0; i < size; i++) {
				 	Response<String> rsp = pipe.lpop(key);
				 	if (rsp == null) {
				 		break;
				 	}
				 	response.add(rsp);
				}	
				pipe.sync();
				
				List<String> list = new LinkedList<>();
				for (Response<String> r : response) {
					String s = r.get();
					if (s == null || s.equalsIgnoreCase("")) {
						break;
					}
					list.add(s);
				}	
				
				return list;
			}
		} catch (Exception e) {
			logger.error("CALL lpop FAIL. Error: " + e.getMessage());
			e.printStackTrace();			
		} finally {
			if (pipe != null) {
				pipe.clear();
			}
			
			R.closeQuietly(pipe);
			R.closeQuietly(jedis);
		}
		
		return Collections.emptyList();
	}
	
	public static String lpop(String key) {
		List<String> list = lpop(key, 1);
		return list == null || list.size() == 0 ? "" : list.get(0);
	}
	
	public static List<String> lrange(String key, long start, long end) {
		if (Check.nullOrEmpty(key)) {
			return Collections.emptyList();
		}
		
		Jedis jedis = getJedis();
		try {
			if (jedis != null) {
				List<String> result = jedis.lrange(key, start, end);
				return result != null ? result : Collections.emptyList();
			}
		} catch (Exception e) {
			logger.error("CALL lrange FAIL. Error: " + e.getMessage());
			e.printStackTrace();			
		} finally {
			R.closeQuietly(jedis);
		}
		
		return Collections.emptyList();
	}
	
	public static List<byte[]> lrange(byte[] key, long start, long end) {
		if (Check.nullOrEmpty(key)) {
			return Collections.emptyList();
		}

		Jedis jedis = getJedis();
		try {
			if (jedis != null) {
				List<byte[]> result = jedis.lrange(key, start, end);
				return result != null ? result : Collections.emptyList();
			}
		} catch (Exception e) {
			logger.error("CALL lrange FAIL. Error: " + e.getMessage());
			e.printStackTrace();			
		} finally {
			R.closeQuietly(jedis);
		}
		
		return Collections.emptyList();		
	}
	
	/**
	 * 获取列表长度
	 * @param key 键值
	 * @return 列表长度
	 */
	public static long llen(String key) {
	    if (Check.nullOrEmpty(key)) {
            return 0;
        }
	    
	    Jedis jedis = getJedis();        
        try {
            if (jedis != null) {
            	Long v = jedis.llen(key);
            	return v != null ? v.longValue() : 0;
            }
        } catch (Exception e) {
            logger.error("CALL existKey FAIL. Error: " + e.getMessage());
            e.printStackTrace();
        } finally {
        	R.closeQuietly(jedis);   
        }
        
	    return 0;
	}
	
	public static List<byte[]> rpop(byte[] key, int size) {
	    if (Check.nullOrEmpty(key) || size <= 0) {
            return Collections.emptyList();
        }

        Jedis jedis = getJedis();
        Pipeline pipe = null;
            
        List<byte[]> list = new LinkedList<>();
        List<Response<byte[]>> rsps = new LinkedList<>();
        try {
            if (jedis != null) {
                if (!jedis.exists(key)) {
                    return Collections.emptyList();
                }
                
                pipe = jedis.pipelined();
                for (int i = 0; i < size; i++) {
                    Response<byte[]> rsp = pipe.rpop(key);
                    if (rsp == null) {
                        break;
                    }
                    rsps.add(rsp);
                }
                pipe.sync();
                
                for (Response<byte[]> r : rsps) {
                    byte[] arr = r.get();
                    if (arr == null || arr.length == 0) {
                        break;
                    }
                    list.add(arr);
                }
            }
        } catch (Exception e) {
        	logger.error("CALL rpop FAIL. Error: " + e.getMessage());
        	e.printStackTrace();
		} finally {
        	R.closeQuietly(pipe);
        	R.closeQuietly(jedis);
        }
        
        return list;   
	}
	
	public static boolean hmset(String key, Map<String, String> map) {
		if (Check.nullOrEmpty(key) || Check.nullOrEmpty(map)) {
			return false;
		}

		Jedis jedis = getJedis();		
		try {
			if (jedis != null) {
				return jedis.hmset(key, map).equalsIgnoreCase("OK");
			}			
		} catch (Exception e) {
			logger.error("CALL hmset FAIL. Error: " + e.getMessage());
			e.printStackTrace();
		} finally {
			R.closeQuietly(jedis);
		}
		
		return false;
	}
	
	public static List<String> hmget(String key, List<String> fields) {
		if (Check.nullOrEmpty(key) || Check.nullOrEmpty(fields)) {
			return Collections.emptyList();
		}

		Jedis jedis = getJedis();
		try {
			if (jedis != null) {
				return jedis.hmget(key, fields.toArray(new String[0]));
			}			
		} catch (Exception e) {
			logger.error("CALL hmget FAIL. Error: " + e.getMessage());
			e.printStackTrace();
		} finally {
			R.closeQuietly(jedis);
		}
		
		return Collections.emptyList();
	}
	
	public static boolean hset(String key, String field, String value) {
		if (Check.nullOrEmpty(key) || Check.nullOrEmpty(field)) {
			return false;
		}

		Jedis jedis = getJedis();

		try {
			if (jedis != null) {
				return jedis.hset(key, field, value) > 0;
			}						
		} catch (Exception e) {
			logger.error("CALL hset FAIL. Error: " + e.getMessage());
			e.printStackTrace();			
		} finally {
			R.closeQuietly(jedis);
		}
		
		return false;
		
	}
	
	public static String hget(String key, String field) {
		if (Check.nullOrEmpty(key) || Check.nullOrEmpty(field)) {
			return "";
		}
		
		String value = null;
		
		Jedis jedis = getJedis();
		try {
			if (jedis != null) {
				value = jedis.hget(key, field);
			}			
		} catch (Exception e) {
			logger.error("CALL hget FAIL. Error: " + e.getMessage());
			e.printStackTrace();			
		} finally {
			R.closeQuietly(jedis);
		}
		
		return value != null ? value : "";
	}
	
	public static Map<String, String> hgets(String key, String[] fields) {
		if (Check.nullOrEmpty(key) || Check.nullOrEmpty(fields)) {
			return Collections.emptyMap();
		}	
		
		Jedis jedis = getJedis();
		try {
			if (jedis != null) {
				List<String> values = jedis.hmget(key, fields);
				if (values != null && values.size() == fields.length) {
					Map<String, String> map = new LinkedHashMap<>();
					for (int i = 0; i < fields.length; i++) {
						map.put(fields[i], values.get(i));
					}
					
					return map;
				}
			}			
		} catch (Exception e) {
			logger.error("CALL hget FAIL. Error: " + e.getMessage());
			e.printStackTrace();			
		} finally {
			R.closeQuietly(jedis);
		}
		
		return Collections.emptyMap();
	}
	
	public static Map<String, String> hgetAll(String key) {
		if (Check.nullOrEmpty(key)) {
			return Collections.emptyMap();
		}
		
		Jedis jedis = getJedis();
		try {
			if (jedis != null) {
				return jedis.hgetAll(key);
			}			
		} catch (Exception e) {
			logger.error("CALL hgetall FAIL. Error: " + e.getMessage());
			e.printStackTrace();			
		} finally {
			R.closeQuietly(jedis);
		}		
		
		return Collections.emptyMap();
	}
	
	public static void main(String[] args) {
		Set<String> keys = Redis.keys("*");
		System.out.println(keys);
	}
}